﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX;
using System.Data;
using System.Collections;
using System.IO;
using SlimDX.Direct3D9;

namespace ThreeDI.UI.Abstraction
{
    // used in ComputeBoundingBox function
    public struct Vertex
    {
        public Vector3 pv;
        public Vector3 nv;
        public float tu1;
        public float tv1;
        public static readonly VertexFormat Format = VertexFormat.Position | VertexFormat.Normal | VertexFormat.Texture0;
        public static readonly int SizeInByte = 8 * sizeof(float);
    }

    public class Mesh : TransformObject
    {
        #region variables

        private SlimDX.Direct3D9.Mesh _Mesh;
        private Texture[] _Texture;
        private Material[] _Material;
        private string _path;
        private Application _app;

        #endregion

        public Vector3 _maxValue;  /// public 3ashan baraba bas 
        public Vector3 _minValue;
        public Vector3 _centerValue;
        float _width, _height, _depth;
        float _halfWidth, _halfHeight, _halfDepth;
        float _radius;

        private Vector3 maxv;
        private Vector3 minv;
        private Vector3 center;
        private float radius;
        private float _x, _y, _z;

        public Mesh(string path, Application application)
            : base(application)
        {
            _path = path;
            _app = application;
        }

        public override void Load()
        {
            _Mesh = SlimDX.Direct3D9.Mesh.FromFile(_app.Device, _path, MeshFlags.SystemMemory);
            ExtendedMaterial[] extendedmaterials = _Mesh.GetMaterials();
            _Material = new Material[extendedmaterials.Length];
            _Texture = new Texture[extendedmaterials.Length];

            for (int i = 0; i < extendedmaterials.Length; i++)
            {
                if (!string.IsNullOrWhiteSpace(extendedmaterials[i].TextureFileName))
                {
                    string _TexturePath = Path.Combine(Path.GetDirectoryName(_path),
                                                       extendedmaterials[i].TextureFileName);
                    _Texture[i] = Texture.FromFile(_app.Device, _TexturePath);

                }
                _Material[i] = extendedmaterials[i].MaterialD3D;
                _Material[i].Ambient = _Material[i].Diffuse;
            }
            ComputeBoundingBox();
            _x = center.X;
            _y = center.Y;
            _z = center.Z;
            Translation(-_x, -_y, -_z);

        }
        public override void Render()
        {
            _app.Device.SetTransform(TransformState.World, TransformationMatrix);
            for (int i = 0; i < _Material.Length; i++)
            {
                if (_Texture[i] != null)
                {
                    _app.Device.SetTexture(0, _Texture[i]);
                }

                _app.Device.Material = _Material[i];
                _Mesh.DrawSubset(i);
            }
           // TransformationMatrix = Matrix.Identity;
        }

        public void ComputeBoundingBox()
        {
            DataStream ds = _Mesh.VertexBuffer.Lock(0, 0, LockFlags.ReadOnly);
            float maxx = 0f, minx = 0f, maxy = 0f, miny = 0f, maxz = 0f, minz = 0f;
            for (int i = 0; i < (ds.Length / 32); i++)
            {
                Vertex tmpv = ds.Read<Vertex>();
                if (maxx > tmpv.pv.X)
                {
                    if (tmpv.pv.X < minx)
                        minx = tmpv.pv.X;
                }
                else if (maxx > tmpv.pv.X && tmpv.pv.X > minx)
                {
                    maxx = tmpv.pv.X;
                }
                else
                {
                    maxx = tmpv.pv.X;
                }
                if (maxy > tmpv.pv.Y)
                {
                    if (tmpv.pv.Y < miny)
                        miny = tmpv.pv.Y;
                }
                else if (maxy > tmpv.pv.Y && tmpv.pv.Y > miny)
                {
                    maxy = tmpv.pv.Y;
                }
                else
                {
                    maxy = tmpv.pv.Y;
                }
                if (maxz > tmpv.pv.Z)
                {
                    if (tmpv.pv.Z < minz)
                        minz = tmpv.pv.Z;
                }
                else if (maxz > tmpv.pv.Z && tmpv.pv.Z > minz)
                {
                    maxz = tmpv.pv.Z;
                }
                else
                {
                    maxz = tmpv.pv.Z;
                }
            }
            _Mesh.VertexBuffer.Unlock();
            ds.Dispose();
            _maxValue = new Vector3(maxx, maxy, maxz);
            _minValue = new Vector3(minx, miny, minz);
            _centerValue = new Vector3(((maxx - minx) / 2) + minx, ((maxy - miny) / 2) + miny, ((maxz - minz) / 2) + minz);
            _radius = (float)(Math.Pow((Math.Pow(Convert.ToDouble(_maxValue.X - _centerValue.X), 2.0) + Math.Pow(Convert.ToDouble(_maxValue.Y - _centerValue.Y), 2.0) + Math.Pow(Convert.ToDouble(_maxValue.Z - _centerValue.Z), 2.0)), 0.5));
           
        }

        public  bool Collision(Mesh mesh1)
        {
            ComputeBoundingBox();
            if ((Math.Abs(mesh1._centerValue.X - this._centerValue.X)) < (mesh1._maxValue.X - mesh1._minValue.X)/2 + (this._maxValue.X - this._minValue.X)/2 &&
               (Math.Abs(mesh1._centerValue.Y - this._centerValue.Y)) < (mesh1._maxValue.Y - mesh1._minValue.Y)/2 + (this._maxValue.Y - this._minValue.Y)/2 &&
               (Math.Abs(mesh1._centerValue.Z - this._centerValue.Z)) < (mesh1._maxValue.Z - mesh1._minValue.Z) / 2 + (this._maxValue.Y - this._minValue.Y)/2)
                return true;
            return false;
        }

        public  bool InverseCollision(Mesh mesh1)
        {
            if ((mesh1._centerValue.X + ((mesh1._maxValue.X - mesh1._minValue.X) / 2)) > this._centerValue.X + ((this._maxValue.X-this._minValue.X) / 2))
                return false;
            if ((mesh1._centerValue.X - ((mesh1._maxValue.X - mesh1._minValue.X) / 2)) < this._centerValue.X - ((this._maxValue.X - this._minValue.X) / 2))
                return false;
            if ((mesh1._centerValue.Y + ((mesh1._maxValue.Y - mesh1._minValue.Y) / 2)) > this._centerValue.Y + ((this._maxValue.Y - this._minValue.Y) / 2))
                return false;
            if ((mesh1._centerValue.Y - ((mesh1._maxValue.Y - mesh1._minValue.Y) / 2)) < this._centerValue.Y - ((this._maxValue.Y - this._minValue.Y) / 2))
                return false;
            if ((mesh1._centerValue.Z + ((mesh1._maxValue.Z - mesh1._minValue.Z) / 2)) > this._centerValue.Z + ((this._maxValue.Z - this._minValue.Z) / 2))
                return false;
            if ((mesh1._centerValue.Z - ((mesh1._maxValue.Z - mesh1._minValue.Z) / 2)) < this._centerValue.Z - ((this._maxValue.Z - this._minValue.Z) / 2))
                return false;
            return true;
        }
    }
}
